import { JSXLexer, Lexer } from "./jsxLexer";
import { Token } from "./token";

// 记忆解析器
abstract class Parser {
    static FAILED: number = -1;
    input: Lexer
    //缓存解析结果
    markers: number[];
    //lookahead:Token[] 
    //List 长度不固定的数组
    lookahead: Token[];
    p: number = 0;;

    constructor(input: Lexer) {
        this.input = input;
        this.markers = [];
        this.lookahead = [];
        //填充lookahead
        this.sync(1);
    }

    /**
     * 1 lookahead自动扩展
     * @param i 
     */
    sync(i: number) {
        if (this.p + i - 1 > this.lookahead.length - 1) {
            //填充下 自动扩展
            let n = (this.p + i - 1) - (this.lookahead.length - 1);
            this.fill(n);
        }
    }
    /**
     * 填充n个Token到lookahead
     * @param n 
     */
    fill(n: number) {
        for (let i = 1; i <= n; i++) {
            this.lookahead.push(this.input.nextToken())
        }
    }

    /**
     * 向前看第i个Token
     * @param i 
     * @returns 
     */
    LT(i: number) {
        this.sync(i);
        return this.lookahead[this.p + i - 1];
    }

    LA(i: number) {
        return this.LT(i).type
    }

    match(tokenType: number) {
        return this.LA(1) == tokenType
    }
    accept(tokenType: number) {
        if (this.LA(1) != tokenType) {
            console.log("expecting", JSXLexer.tokenNames[tokenType]);
            console.log("found", this.input.nextToken());
            throw new Error("Parser match error")
        }
        return this.LA(1) == tokenType
    }
    consume() {
        this.p = this.p + 1;
        if (this.p == this.lookahead.length && !this.isSpeculating()) {
            this.p = 0;
            this.lookahead = [];
        }
        this.sync(1);
    }

    /**
     * 2 回溯的过程 
     * [p1,p2,p3,p4]
     */
    mark() {
        this.markers.push(this.p);
        return this.p;
    }

    release() {
        let marker = this.markers[this.markers.length - 1];
        this.markers.pop();
        this.seek(marker);
    }
    seek(index: number) {
        this.p = index;
    }

    isSpeculating() {
        return this.markers.length > 0
    }

    memoize(
        memoization: Map<number, number>,
        startTokenIndex: number,
        failed: boolean) {
        let stopTokenIndex = failed ? Parser.FAILED : this.index();
        memoization.set(startTokenIndex, stopTokenIndex);

    }
    alreadyParsedRule(
        memoization: Map<number, number>
    ) {
        let memoI = memoization.get(this.index());
        if (memoI == null) {
            return false
        }
        console.log("parsed before at index " + this.index()
            + "skip to token index" + memoI + this.lookahead[memoI]);
        if (memoI == Parser.FAILED) {
            throw new Error("already error")
        }
        //跳转到成功解析的位置
        this.seek(memoI);
        return true;
    }
    index() {
        return this.p;
    }
    abstract clearMemo(): void;
}

class JSXParser extends Parser {
    list_memor: Map<number, number> = new Map();
    clearMemo(): void {
        this.list_memor.clear();
    }
    constructor(input: JSXLexer) {
        super(input);
        this.input = input;
        this.markers = [];
        this.lookahead = [];
        //填充lookahead
        this.sync(1);
    }

    // jsx: block+;
    jsx() {
        let success = true;
        //匹配开始
        this.mark();
        try {
            success = this.block();
            while (success) {
                success = this.block();
            }
        } catch (e) {
            success = false;
        }
        this.release();
        return success;
    }
    // block: js | blockJsx | htmlElement  ;
    block() {
        let success = true;

        return success;
    }
    // js: JS;
    js() {
        return this.accept(JSXLexer.CODE);
    }
    // blockJsx: LP jsx RP;
    blockJsx() {
        return this.accept(JSXLexer.LP)
            && this.jsx()
            && this.accept(JSXLexer.RP);
    }
    // htmlElement:htmlWithChildren | htmlNoChildren;
    htmlElement() {
        return this.htmlWithChildren()
            || this.htmlNoChildren()
    }
    // htmlWithChildren:
    htmlWithChildren() {
        return this.htmlStart()
            && this.htmlAttributes()
            && this.accept(JSXLexer.GT)
            && this.htmlContent()

            && this.accept(JSXLexer.LT)
            && this.accept(JSXLexer.NAME)
            && this.accept(JSXLexer.GT)
    }
    // htmlNoChildren:
    htmlNoChildren() {
        return this.htmlStart()
            && this.htmlAttributes()
            && this.accept(JSXLexer.SE)
            && this.accept(JSXLexer.GT)
    }
    // htmlStart: HTML_TAG_OPEN | TAG_OPEN;
    htmlStart() {
        return this.accept(JSXLexer.LT)
            && this.accept(JSXLexer.NAME)
    }
    // htmlAttributes: htmlAttribute*;
    htmlAttributes() {
        let success = true;
        while (success) {
            success = this.htmlAttribute()
        }
        success = true;
        return success;
    }
    // htmlAttribute:
    htmlAttribute() {
        let success = true;

        success = (this.accept(JSXLexer.NAME)
            && this.accept(JSXLexer.EQ)
            && this.htmlAttrValue())
            || this.accept(JSXLexer.NAME)

        return success;
    }
    // htmlAttrValue
    htmlAttrValue() {
        let success = true;

        success = this.accept(JSXLexer.STRING)
            || this.blockJsx()

        return success;
    }
    // htmlContent
    htmlContent(): boolean {
        let success = true;
        this.htmlChardata();

        if (this.match(JSXLexer.LT)) {
            success = this.htmlElement();
        } else if (this.match(JSXLexer.LP)) {
            success = this.htmlContentJsx();
        }
        while (success) {
            if (this.match(JSXLexer.LT)) {
                success = this.htmlElement();
            } else if (this.match(JSXLexer.LP)) {
                success = this.htmlContentJsx();
            }
        }

        this.htmlChardata();
        success = true;
        return success;
    }
    // htmlChardata
    htmlChardata() {
        let success = true;
        success = this.accept(JSXLexer.TEXT)
        return success;
    }
    // htmlContentJsx: HTML_LP jsx RP;
    htmlContentJsx() {
        return this.accept(JSXLexer.LP)
            && this.jsx()
            && this.accept(JSXLexer.RP);
    }
}

export {
    JSXParser
}